<template>
  <div class="editor-box">
    <div class="header">
      <div class="header-name">{{flowName}}</div>
      <div class="header-button">
        <el-button ref="buttonRef" type="warning">
          <el-badge
            :value="errorList.length"
            :hidden="errorList.length === 0"
            :offset="[15, -5]"
            style="display: flex;align-items: center"
          >
            <ac-icon iconId="018ece4f6f295a56438f" />
            <span>问题</span>
          </el-badge>
        </el-button>
        <el-button @click="debug" type="success">
          <ac-icon iconId="018ece4f7121ccd28153" /> 调试
        </el-button>
        <el-button @click="save" type="primary">
          <ac-icon iconId="018ece4f68eaa77e4813" /> 保存
        </el-button>
        <el-button @click="quit" type="danger">
          <ac-icon iconId="018ece4f732eee924b02" /> 退出
        </el-button>
        <el-popover
          :virtual-ref="buttonRef"
          virtual-triggering
          trigger="click"
          popper-class="error-box"
          width="400px"
          placement="top"
        >
          <template #default>
            <div style="overflow:auto;max-height: calc(100vh - 200px)">
              <div class="title">问题清单</div>
              <div v-for="(error, index) in errorList" class="error-item" :key="index">
                <div class="error-title">{{error.name}}</div>
                <div
                  class="error-info"
                  v-for="(item, index2) in error.list"
                  :key="index2"
                >{{item}}</div>
              </div>
              <div v-if="errorList.length === 0" class="empty">暂未发现问题</div>
            </div>
          </template>
        </el-popover>
      </div>
    </div>
    <VueFlow
      v-if="!initialized"
      v-model:nodes="nodes"
      v-model:edges="edges"
      :edge-types="edgeTypes"
      :default-edge-options="edgeOptions"
      :connection-line-style="{ stroke: '#AFAFAF', strokeWidth: 2, strokeDasharray: 'none' }"
      :connectable="true"
      :nodes-connectable="true"
      :edges-updatable="true"
      :connect-on-click="true"
      @pane-click="paneClick"
      fit-view
    >
      <Background variant="dots" :gap="15" bgColor="#f7f7f780" />
      <Controls />
      <template #node-custom="node">
        <div
          :class="[
            'custom-node',
            currNode.id === node.id ? 'active' : '',
            debugMode && node.data.success ? 'success' : (node.data.success === false ? 'error' : '')
          ]"
        >
          <div class="node-box" @click="nodeClick(node)">
            <div class="node-header">
              <div class="title">
                <ac-icon
                  :iconId="node.data.iconId"
                  :style="`background-color: ${node.data.iconColor}`"
                  color="white"
                  size="10"
                  class="icon-box"
                />
                <span class="name-box">{{ node.label }}</span>
              </div>
              <div class="button" @click.stop>
                <el-dropdown trigger="click" v-if="node.data.type !== '00'" >
                  <span>
                    <ac-icon iconId="018dca6283a1e534a046" size="16" />
                  </span>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item @click.stop="copyNode(node)">
                        <ac-icon
                          title="复制"
                          size="14"
                          iconId="018ece4f6cdc1f74eab3"
                          color="#67C23A"
                          style="margin-right: 8px"
                        /> 复制
                      </el-dropdown-item>
                      <el-dropdown-item @click.stop="deleteNode(node)" :divided="true">
                        <ac-icon
                          iconId="018dc9c3efd3a0502886"
                          title="删除"
                          size="14"
                          color="red"
                          style="margin-right: 8px"
                        />删除
                      </el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </div>
            </div>
            <div
              v-for="(classifier, index) in node.data?.classifierList"
              :key="index"
              class="classifier-item"
            >
              <div class="classifier-name">分类 {{index + 1}}</div>
              <div class="classifier-content">{{classifier.content}}</div>
            </div>
            <div
              v-for="(condition, index) in node.data?.conditionList"
              :key="index"
              class="classifier-item"
            >
              <div class="classifier-name">分支条件 {{index + 1}}</div>
              <div class="classifier-content">{{condition.name}}</div>
            </div>
            <div
              v-if="node.data.type === '02' || node.data.type === '21'"
              class="classifier-item"
            >
              <div class="classifier-name">ELSE</div>
            </div>
          </div>
          <Handle
            v-if="node.data.type !== '00'"
            type="target"
            position="left"
            class="left"
            :id="'target_handle_' + node.id"
            :connectableStart="false"
            :connectableEnd="true"
          >
            <template #default>
              <ac-icon iconId="018dc9c3efed46e8b931" size="15" />
            </template>
          </Handle>
          <Handle
            v-if="node.data.type !== '99' && !node.data?.classifierList && !node.data?.conditionList"
            :id="'source_handle_' + node.id"
            type="source"
            position="right"
            :connectableStart="true"
            :connectableEnd="false"
          >
            <template #default>
              <div class="right">
                <el-popover
                  v-model:visible="node.data.popoverVisible"
                  trigger="focus"
                  placement="right"
                  popper-class="popover-box"
                  :show-arrow="false"
                >
                  <template #reference>
                    <ac-icon
                      iconId="018dc9c3efed46e8b931"
                      size="15"
                      @click="node.data.popoverVisible = true"
                    />
                  </template>
                  <template #default>
                    <div
                      class="popover-box-item"
                      v-for="(nodeType, index) in nodeTypeList"
                      :key="index"
                    >
                      <el-divider v-if="nodeType.type === 'divider'" style="margin: 0" />
                      <div
                        v-else
                        class="popover-item"
                        @click="addNode('source_handle_' + node.id, nodeType, node)"
                      >
                        <ac-icon
                          :iconId="nodeType.iconId"
                          :style="`background-color: ${nodeType.iconColor}`"
                          color="white"
                          size="15"
                          class="icon-box"
                        />
                        <span class="name-box">{{nodeType.name}}</span>
                      </div>
                    </div>
                  </template>
                </el-popover>
              </div>
            </template>
          </Handle>
          <Handle
            v-for="(classifier, index) in node.data?.classifierList"
            :key="index"
            :id="'source_handle_' + classifier.id"
            type="source"
            position="right"
            :connectableStart="true"
            :connectableEnd="false"
            :style="{top: 0, marginTop: (46 + 44 * index) + 'px'}"
          >
            <template #default>
              <div class="right">
                <el-popover
                  v-model:visible="classifier.popoverVisible"
                  trigger="focus"
                  placement="right"
                  popper-class="popover-box"
                  :show-arrow="false"
                >
                  <template #reference>
                    <ac-icon
                      iconId="018dc9c3efed46e8b931"
                      size="15"
                      @click="classifier.popoverVisible = true"
                    />
                  </template>
                  <template #default>
                    <div
                      class="popover-box-item"
                      v-for="(nodeType, index2) in nodeTypeList"
                      :key="index2"
                    >
                      <el-divider v-if="nodeType.type === 'divider'" style="margin: 0" />
                      <div
                        v-else
                        class="popover-item"
                        @click="addNode('source_handle_' + classifier.id, nodeType, node)"
                      >
                        <ac-icon
                          :iconId="nodeType.iconId"
                          :style="`background-color: ${nodeType.iconColor}`"
                          color="white"
                          size="15"
                          class="icon-box"
                        />
                        <span class="name-box">{{nodeType.name}}</span>
                      </div>
                    </div>
                  </template>
                </el-popover>
              </div>
            </template>
          </Handle>
          <Handle
            v-for="(condition, index) in node.data?.conditionList"
            :key="index"
            :id="'source_handle_' + condition.id"
            type="source"
            position="right"
            :connectableStart="true"
            :connectableEnd="false"
            :style="{top: 0, marginTop: (46 + 44 * index) + 'px'}"
          >
            <template #default>
              <div class="right">
                <el-popover
                  v-model:visible="condition.popoverVisible"
                  trigger="focus"
                  placement="right"
                  popper-class="popover-box"
                  :show-arrow="false"
                >
                  <template #reference>
                    <ac-icon
                      iconId="018dc9c3efed46e8b931"
                      size="15"
                      @click="condition.popoverVisible = true"
                    />
                  </template>
                  <template #default>
                    <div
                      class="popover-box-item"
                      v-for="(nodeType, index2) in nodeTypeList"
                      :key="index2"
                    >
                      <el-divider v-if="nodeType.type === 'divider'" style="margin: 0" />
                      <div
                        v-else
                        class="popover-item"
                        @click="addNode('source_handle_' + condition.id, nodeType, node)"
                      >
                        <ac-icon
                          :iconId="nodeType.iconId"
                          :style="`background-color: ${nodeType.iconColor}`"
                          color="white"
                          size="15"
                          class="icon-box"
                        />
                        <span class="name-box">{{nodeType.name}}</span>
                      </div>
                    </div>
                  </template>
                </el-popover>
              </div>
            </template>
          </Handle>
          <Handle
            v-if="(node.data.type === '02' && node.data?.classifierList?.length > 0)
              || (node.data.type === '21' && node.data?.conditionList?.length > 0)"
            :id="'source_handle_' + node.id"
            type="source"
            position="right"
            :connectableStart="true"
            :connectableEnd="false"
            :style="{top: 0, marginTop: (46 + 44
              * (node.data.type === '02' ? node.data?.classifierList?.length : node.data?.conditionList?.length)) + 'px'}"
          >
            <template #default>
              <div class="right">
                <el-popover
                  v-model:visible="node.data.popoverVisible"
                  trigger="focus"
                  placement="right"
                  popper-class="popover-box"
                  :show-arrow="false"
                >
                  <template #reference>
                    <ac-icon
                      iconId="018dc9c3efed46e8b931"
                      size="15"
                      @click="node.data.popoverVisible = true"
                    />
                  </template>
                  <template #default>
                    <div
                      class="popover-box-item"
                      v-for="(nodeType, index2) in nodeTypeList"
                      :key="index2"
                    >
                      <el-divider v-if="nodeType.type === 'divider'" style="margin: 0" />
                      <div
                        v-else
                        class="popover-item"
                        @click="addNode('source_handle_' + node.id, nodeType, node)"
                      >
                        <ac-icon
                          :iconId="nodeType.iconId"
                          :style="`background-color: ${nodeType.iconColor}`"
                          color="white"
                          size="15"
                          class="icon-box"
                        />
                        <span class="name-box">{{nodeType.name}}</span>
                      </div>
                    </div>
                  </template>
                </el-popover>
              </div>
            </template>
          </Handle>
        </div>
      </template>
    </VueFlow>
  </div>

  <el-drawer
    v-if="paramVisible"
    v-model="paramVisible"
    title="节点设置"
    size="500px"
    destroy-on-close
    class="no-padding"
    :close-on-click-modal="true"
    modal-class="no-mask margin-top-50"
    append-to-body
  >
    <div v-if="currNode.id" class="drawer-body">
      <div class="header">
        <div class="title">
          <ac-icon
            :iconId="currNode.data?.iconId"
            :style="`background-color: ${currNode.data?.iconColor}`"
            color="white"
            size="16"
            class="icon-box"
          />
          <div class="name-box">
            <el-input
              v-if="paramLabelEdit"
              ref="paramLabel"
              v-model="paramLabelValue"
              clearable
              @input="changeLabel"
              @blur="paramLabelEdit = false"
            />
            <span v-else>{{currNode.label}}</span>
            <ac-icon
              v-if="!paramLabelEdit"
              iconId="018ece4f6918a49a759b"
              size="18"
              title="编辑"
              @click="editLabel"
            />
          </div>
        </div>
      </div>
      <Component
        :is="currNodeComp"
        v-bind:data="currNode.data"
        v-bind:sourceParamOptions="sourceParamOptions"
      />
    </div>
  </el-drawer>

  <el-drawer
    v-if="debugVisible"
    v-model="debugVisible"
    title="调试"
    size="500px"
    destroy-on-close
    direction="ltr"
    class="no-padding"
    modal-class="no-mask margin-top-50"
    append-to-body
    :close-on-click-modal="true"
    @close="closeDebug"
  >
    <div class="drawer-body">
      <el-tabs v-model="tabName">
        <el-tab-pane label="输入" name="input" aria-hidden="false">
          <el-form
            ref="debugForm"
            :model="debugData"
            label-width="85px"
            :inline="false"
            label-position="right"
          >
            <el-form-item
              v-for="(field, index) in fieldList"
              :key="index"
              :label="field.name"
              :prop="field.name"
              :rules="field.required ? [{required: true, message: '此项不能为空！', trigger: ['change', 'blur']}] : []"
            >
              <el-input v-if="field.type === '文本'" v-model="debugData[field.name]" />
              <ac-image v-if="field.type === '图片'" v-model="debugData[field.name]" style="width: 80px;height: 80px"/>
              <ac-file v-if="field.type === '文件'" v-model="debugData[field.name]" />
            </el-form-item>

            <el-button
              size="large"
              type="primary"
              style="width: 100%;margin-top: 20px;"
              :loading="debugLoading"
              @click.prevent="doDebug"
            >
              <ac-icon
                iconId="018ece4f7121ccd28153"
                size="18"
                style="margin-right: 5px"
              /> 开始调试
            </el-button>
          </el-form>
        </el-tab-pane>
        <el-tab-pane label="详情" name="detail">
          <div class="debug-box" v-if="debugResult.code">
            <div class="line">状态：
              <span :class="debugResult.code === '1' ? 'success' : 'failure'">
                {{debugResult.code === '1' ? '调试成功' : '调试失败'}}
              </span>
            </div>
            <div class="line">耗时：{{tool.formatTime(debugResult.time)}}</div>
            <div v-if="Object.keys(debugResult.input).length > 0">输入参数：</div>
            <div v-if="Object.keys(debugResult.input).length > 0" class="json-box">
              <pre>{{JSON.stringify(debugResult.input, null, 4)}}</pre>
            </div>
            <div v-if="Object.keys(debugResult.output).length > 0">输出参数：</div>
            <div v-if="Object.keys(debugResult.output).length > 0" class="json-box">
              <pre>{{JSON.stringify(debugResult.output, null, 4)}}</pre>
            </div>
            <div v-if="debugResult.content">输出结果：</div>
            <div v-if="debugResult.content" class="json-box"><pre>{{debugResult.content}}</pre></div>
            <div v-if="debugResult.message">出错信息：</div>
            <div v-if="debugResult.message" class="json-box"><pre>{{debugResult.message}}</pre></div>
          </div>
        </el-tab-pane>
        <el-tab-pane label="跟踪" name="track">
          <div class="track-box" v-if="debugResult.code">
            <el-collapse v-model="collapseName">
              <el-collapse-item
                v-for="(node, index) in debugResult.nodeList"
                :key="index"
                :title="node.nodeId"
                :name="node.nodeId"
              >
                <template #title>
                  <div class="title">
                    <div
                      :class="['node-name', node.code !== '1' ? 'error' : '']"
                    >{{node.nodeName}}</div>
                    <div class="time">耗时：{{tool.formatTime(node.time)}}</div>
                  </div>
                </template>
                <div v-if="Object.keys(node.input).length > 0">输入参数：</div>
                <div v-if="Object.keys(node.input).length > 0" class="json-box">
                  <pre>{{JSON.stringify(node.input, null, 4)}}</pre>
                </div>
                <div v-if="Object.keys(node.output).length > 0">输出参数：</div>
                <div v-if="Object.keys(node.output).length > 0" class="json-box">
                  <pre>{{JSON.stringify(node.output, null, 4)}}</pre>
                </div>
                <div v-if="node.content">输出结果：</div>
                <div v-if="node.content" class="json-box"><pre>{{node.content}}</pre></div>
                <div v-if="node.message">出错信息：</div>
                <div v-if="node.message" class="json-box"><pre>{{node.message}}</pre></div>
              </el-collapse-item>
            </el-collapse>
          </div>
        </el-tab-pane>
      </el-tabs>
    </div>
  </el-drawer>
</template>

<script setup>
import { ref, onMounted, nextTick, markRaw, watch, computed } from 'vue';
import { Handle, VueFlow, useVueFlow } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import { Controls } from '@vue-flow/controls';
import { ElMessage, ElMessageBox } from "element-plus";
import api from "@/http/api";
import tool from '@/utils/tool';
import CustomEdge from '@/admin/vueflow/customEdge.vue'
import "@vue-flow/core/dist/style.css";
import '@vue-flow/core/dist/theme-default.css'
import '@vue-flow/controls/dist/style.css';
import Start from "@/admin/ai/components/start.vue";
import Llm from "@/admin/ai/components/llm.vue";
import Classifier from "@/admin/ai/components/classifier.vue";
import Condition from "@/admin/ai/components/condition.vue";
import Http from "@/admin/ai/components/http.vue";
import Knowledge from "@/admin/ai/components/knowledge.vue";
import Reply from "@/admin/ai/components/reply.vue";
import Script from "@/admin/ai/components/script.vue";
import End from "@/admin/ai/components/end.vue";

const emit = defineEmits(['quit']);
const props = defineProps({
  id: String
});
const edgeTypes = { custom: markRaw(CustomEdge) };
const initialized = ref(true);
const buttonRef = ref();
const flowName = ref('');
const nodes = ref([]);
const edges = ref([]);
const hasChange = ref(false);
const currNode = ref({});
const paramVisible = ref(false);
const paramLabel = ref();
const paramLabelEdit = ref(false);
const paramLabelValue = ref('');
const currNodeComp = ref(null);

const debugVisible = ref(false);
const debugMode = ref(false);
const fieldList = ref([]);
const debugForm = ref();
const debugData = ref({});
const debugResult = ref({});
const debugLoading = ref(false);
const tabName = ref('input');
const collapseName = ref('');

const edgeOptions = ref({
  type: 'custom',
  selectable: true,
  deletable: true,
  focusable: true,
  updatable: true
});
const nodeTypeList = ref([
  {type: '01', iconId: '018dca3b4707c4c43a83', iconColor: '#13c99d', name: 'LLM'},
  {type: '02', iconId: '018ece4f6cf54522d477', iconColor: '#13c99d', name: '分类器'},
  {type: 'divider'},
  {type: '11', iconId: '018ece4f71ab6c23fc91', iconColor: '#3d61d7', name: '知识库'},
  {type: 'divider'},
  {type: '21', iconId: '018ece4f68a143305631', iconColor: '#2fc4fa', name: '条件分支'},
  {type: '22', iconId: '018ece4f6a74033c855e', iconColor: '#2fc4fa', name: '脚本执行'},
  {type: '23', iconId: '018ece4f6e97a0e0b351', iconColor: '#2fc4fa', name: 'HTTP请求'},
  {type: 'divider'},
  {type: '41', iconId: '018ece4f6c4954d8f1d5', iconColor: '#12cb9e', name: '直接回复'},
  {type: 'divider'},
  {type: '99', iconId: '018f9ae7fa11010b3931', iconColor: '#f34b4b', name: '结束'},
]);
const nodeTypeParamList = ref([
  {
    type: '01',
    outputList: [
      {type: '文本', name: '结果'}
    ]
  },
  {
    type: '02',
    classifierList: [{content: ''}, {content: ''}],
    outputList: [
      {type: '整数', name: '分类索引'},
      {type: '文本', name: '分类内容'},
    ]
  },
  {
    type: '11',
    outputList: [
      {type: '数组', name: '文档列表'},
      {type: '文本', name: '文档内容'},
    ]
  },
  {
    type: '21',
    outputList: [
      {type: '整数', name: '分支索引'},
      {type: '文本', name: '分支名称'},
    ]
  },
  {
    type: '23',
    outputList: [
      {type: '对象', name: '请求返回结果', builtin: true},
      {type: '整数', name: '请求返回状态码', builtin: true},
    ]
  },
]);

const { onConnect, addEdges } = useVueFlow();

onConnect((params) => {
  addEdges([params])
});

const paneClick = () => {
  paramVisible.value = false;
  nextTick(() => currNode.value = {label: ''});
  validateData();
};

const nodeClick = (node) => {
  paramVisible.value = false;
  currNode.value = node;
  currNodeComp.value = getTypeComp(currNode.value.data.type);
  paramLabelValue.value = currNode.value.label;
  nextTick(() => paramVisible.value = true);
  validateData();
};

const editLabel = () => {
  paramLabelValue.value = currNode.value.label;
  paramLabelEdit.value = true;
  nextTick(() => paramLabel.value.focus());
};

const changeLabel = () => {
  const node = nodes.value.find(item => item.id === currNode.value.id);
  if (node) node.label = paramLabelValue.value;
};

const getTypeComp = (type) => {
  switch (type) {
    case '00': return markRaw(Start);
    case '01': return markRaw(Llm);
    case '02': return markRaw(Classifier);
    case '11': return markRaw(Knowledge);
    case '21': return markRaw(Condition);
    case '22': return markRaw(Script);
    case '23': return markRaw(Http);
    case '41': return markRaw(Reply);
    case '99': return markRaw(End);
  }
  return null;
};

const sourceParamOptions = computed(() => {
  if (!currNode.value || !currNode.value.id) return [];

  const options = [];
  const nodeIdSet = new Set();
  fillParams(currNode.value.id, options, nodeIdSet);
  return options;
});

const fillParams = (nodeId, options, nodeIdSet) => {
  const parentIds = findParentNodeId(nodeId) || [];
  parentIds.forEach(id => {
    if (nodeIdSet.has(id)) return;
    nodeIdSet.add(id);

    const node = nodes.value.find(item => item.id === id);
    if (node) {
      if (node.data?.outputList?.length > 0) {
        for (const param of node.data.outputList) {
          if (!param.id || !param.name) continue;

          options.push({
            key: node.id + '.' + param.name,
            value: node.label + ' / ' + (param.name || param.fieldName)
          });
        }
      }
    }
  });

  parentIds.forEach(id => {
    fillParams(id, options, nodeIdSet);
  });
};

const findParentNodeId = (nodeId) => {
  return edges.value.filter(edge => edge.target === nodeId).map(edge => edge.source);
};

const getNodeParam = (type) => {
  const param = tool.deepClone(nodeTypeParamList.value.find(item => item.type === type)) || {};
  (param.classifierList || []).forEach(item => item.id = tool.uuid());
  (param.inputList || []).forEach(item => item.id = tool.uuid());
  (param.outputList || []).forEach(item => item.id = tool.uuid());
  return param || {};
};

const addNode = (sourceHandle, nodeType, node) => {
  node.data.popoverVisible = false;

  const id = tool.uuid();
  let x = node.position.x + 300;
  let y = node.position.y;

  for (const edge of edges.value) {
    if (edge.source === node.id) {
      const targetNode = nodes.value.find(item => item.id === edge.target);
      if (targetNode && targetNode.position
          && targetNode.position.x >= x - 200
          && targetNode.position.x <= x + 200
          && targetNode.position.y > y - 100) {
        y = targetNode.position.y + 100;
      }
    }
  }

  nodes.value.push({
    id: id,
    type: 'custom',
    label: nodeType.name,
    data: {
      ...tool.deepClone(nodeType),
      ...getNodeParam(nodeType.type)
    },
    position: {x: x, y: y}
  });

  edges.value.push({
    id: tool.uuid(),
    source: node.id,
    target: id,
    sourceHandle: sourceHandle,
    targetHandle: 'target_handle_' + id
  });
};

const copyNode = (node) => {
  nodes.value.push({
    id: tool.uuid(),
    type: node.type,
    label: node.label,
    data: {
      ...tool.deepClone(node.data),
      ...getNodeParam(node.data.type)
    },
    position: {x: node.position.x, y: node.position.y + 120}
  });
};

const deleteNode = (node) => {
  if (!node || !node.id) return;

  ElMessageBox.confirm(`请确认是否删除节点【${node.label}】？`, '提示', {type: 'info'}).then(() => {
    const index = nodes.value.findIndex(item => item.id === node.id);
    nodes.value.splice(index, 1);
    edges.value = edges.value.filter(edge => edge.source !== node.id && edge.target !== node.id);
  }).catch(() => {});
};

const loadData = async() => {
  if (!props.id) return;

  nodes.value = [];
  edges.value = [];
  hasChange.value = false;
  const res = await api.post('/system/aiFlow/queryBy', {id: props.id});
  flowName.value = res.data?.name || '';
  if (res.data?.jsonText) {
    const json = JSON.parse(res.data?.jsonText);
    nodes.value = json.nodes;
    edges.value = json.edges;
  } else {
    nodes.value.push({
      id: tool.uuid(),
      type: 'custom',
      label: '开始',
      data: {
        type: '00',
        iconId: '018ece4f7121ccd29312',
        iconColor: '#46a6ff',
        outputList: [
          {id: 'question', name: '用户问题', type: '文本', required: true, builtin: true},
        ]
      },
      position: {x: 100, y: 100}
    });
  }
};

const debug = () => {
  validateData();
  if (errorList.value?.length > 0) {
    ElMessage.error("请先修复问题清单再进行调试！");
    return;
  }

  const startNode = nodes.value.find(item => item.data?.type === '00');
  fieldList.value = startNode?.data?.outputList || [];
  debugVisible.value = true;
};

const doDebug = () => {
  debugForm.value?.validate(valid => {
    if (!valid) return;

    nodes.value.forEach(node => {
      node.data.success = null;
    });

    edges.value.forEach(edge => {
      edge.class = null;
    });

    debugLoading.value = true;
    debugMode.value = true;
    debugResult.value = {};
    const data = {
      flowJsonText: JSON.stringify(getFlowData()),
      chartData: debugData.value
    };
    api.post('/ai/flow/debug', data, 5 * 60 * 1000).then(res => {
      debugResult.value = res.data || {};
      const nodeList = res.data?.nodeList || [];
      const lineList = res.data?.lineList || [];

      let code = '1';
      nodes.value.forEach(node => {
        const find = nodeList.find(item => node.id === item.nodeId);
        if (!find) return;
        node.data.success = find.code === '1' || false;
        code = find?.code === '0' ? '0' : code;
      });
      debugResult.value.code = code;

      edges.value.forEach(edge => {
        const find = lineList.find(item => edge.id === item.id);
        if (!find) return;
        edge.class = find ? 'success' : '';
      });

      debugLoading.value = false;
      nextTick(() => tabName.value = 'detail');
    }).catch(() => debugLoading.value = false);
  });
};

const closeDebug = () => {
  edges.value.forEach(item => item.class = '');
  debugMode.value = false;
};

const save = () => {
  const json = getFlowData();
  const data = {id: props.id, jsonText: JSON.stringify(json)};
  api.post('/system/aiFlow/saveJson', data).then(res => {
    hasChange.value = false;
    ElMessage.success(res.message);
  });
};

const getFlowData = () => {
  return {
    nodes: tool.deepClone(nodes.value, prop => prop !== 'success') || [],
    edges: tool.deepClone(
      edges.value,
      prop => prop !== 'sourceNode' && prop !== 'targetNode' && prop !== 'class'
    ) || []
  };
};

const quit = () => {
  if (hasChange.value) {
    ElMessageBox.confirm("数据未保存请确认是否退出？", "提示", { type: "warning" }).then(() => {
      emit('quit');
    }).catch(() => {});
  } else {
    emit('quit');
  }
};

const validator = {
  '00': (node) => validateStart(node),
  '01': (node) => validateLlm(node),
  '02': (node) => validateClassifier(node),
  '11': (node) => validateKnowledge(node),
  '21': (node) => validateCondition(node),
  '22': (node) => validateScript(node),
  '23': (node) => validateHttp(node),
  '41': (node) => validateReply(node),
  '99': (node) => validateEnd(node),
}
const errorList = ref([]);
const validateData = () => {
  try {
    errorList.value = [];
    (nodes.value || []).forEach(node => {
      node.data?.type && validator[node.data.type] && validator[node.data.type](node);
    });
  } catch (e) {
    console.log(e);
  }
};

const validateStart = (node) => {
  const result = [];
  (node.data.outputList || []).forEach((item, index) => {
    if (!item.type) {
      result.push(`【输入字段】第 ${index + 1} 行【类型】不能为空！`)
    }
    if (!item.name) {
      result.push(`【输入字段】第 ${index + 1} 行【字段名称】不能为空！`)
    }
  });
  if (!edges.value.find(edge => edge.source === node.id)) {
    result.push("【连线】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '开始节点', list: result});
  }
};

const validateLlm = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
    if (!item.sourceParamId) {
      result.push(`【输入变量】第 ${index + 1} 行【变量来源】不能为空！`)
    }
  });
  if (!node.data.modelId) {
    result.push(`【模型名称】不能为空！`)
  }
  if (!node.data.userPrompt) {
    result.push(`【用户提示词】不能为空！`)
  }
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (!edges.value.find(edge => edge.source === node.id)) {
    result.push("【连线】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || 'LLM', list: result});
  }
};

const validateClassifier = (node) => {
  const result = [];
  if (!node.data.sourceParamId) {
    result.push(`【输入变量】不能为空！`)
  }
  if (!node.data.modelId) {
    result.push(`【模型名称】不能为空！`)
  }
  if (!node.data.classifierList || node.data.classifierList.length === 0) {
    result.push(`【分类】不能为空！`)
  }
  (node.data.classifierList || []).forEach((item, index) => {
    if (!item.content) {
      result.push(`【分类】第 ${index + 1} 行【分类主题内容】不能为空！`)
    }
  });
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  (node.data.classifierList || []).forEach((item, index) => {
    if (!edges.value.find(edge => edge.sourceHandle === 'source_handle_' + item.id)) {
      result.push(`【连线】【分类 ${index + 1}】出线未建立！`);
    }
  });
  if (!edges.value.find(edge => edge.source === node.id && edge.sourceHandle === 'source_handle_' + node.id)) {
    result.push("【连线】【ELSE】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '分类器', list: result});
  }
};

const validateKnowledge = (node) => {
  const result = [];
  if (!node.data.sourceParamId) {
    result.push(`【输入变量】不能为空！`);
  }
  if (!node.data.knowledgeList || node.data.knowledgeList.length === 0) {
    result.push(`【知识库】不能为空！`);
  }
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (!edges.value.find(edge => edge.source === node.id)) {
    result.push("【连线】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '知识库', list: result});
  }
};

const validateCondition = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
    if (!item.sourceParamId) {
      result.push(`【输入变量】第 ${index + 1} 行【变量来源】不能为空！`)
    }
  });
  if (!node.data.conditionList || node.data.conditionList.length === 0) {
    result.push(`【条件】不能为空！`)
  }
  (node.data.conditionList || []).forEach((item, index) => {
    (item.groupList || []).forEach(condition => {
      if (!condition.name) {
        result.push(`【条件】【分支条件 ${index + 1}】【参数来源】不能为空！`)
      }
      if (!condition.type) {
        result.push(`【条件】【分支条件 ${index + 1}】【条件类型】不能为空！`)
      }
    });
  });
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  (node.data.conditionList || []).forEach((item, index) => {
    if (!edges.value.find(edge => edge.sourceHandle === 'source_handle_' + item.id)) {
      result.push(`【连线】【条件分支 ${index + 1}】出线未建立！`);
    }
  });
  if (!edges.value.find(edge => edge.source === node.id && edge.sourceHandle === 'source_handle_' + node.id)) {
    result.push("【连线】【ELSE】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '分类器', list: result});
  }
};

const validateScript = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
  });
  if (!node.data.scriptId) {
    result.push(`【脚本名称】不能为空！`)
  }
  (node.data.outputList || []).forEach((item, index) => {
    if (!item.type) {
      result.push(`【输出变量】第 ${index + 1} 行【变量类型】不能为空！`)
    }
    if (!item.name) {
      result.push(`【输出变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
  });
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (!edges.value.find(edge => edge.source === node.id)) {
    result.push("【连线】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '脚本执行', list: result});
  }
};

const validateHttp = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
  });
  if (!node.data.method) {
    result.push(`【API】【请求方法】不能为空！`)
  }
  if (!node.data.url) {
    result.push(`【API】【API地址】不能为空！`)
  }
  (node.data.headerList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【请求头】第 ${index + 1} 行【参数名】不能为空！`)
    }
  });
  (node.data.paramList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【请求参数】第 ${index + 1} 行【参数名】不能为空！`)
    }
  });
  if (!node.data.bodyType) {
    result.push(`【请求体】不能为空！`)
  }
  (node.data.outputList || []).forEach((item, index) => {
    if (!item.type) {
      result.push(`【输出变量】第 ${index + 1} 行【变量类型】不能为空！`)
    }
    if (!item.name) {
      result.push(`【输出变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
  });
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (!edges.value.find(edge => edge.source === node.id)) {
    result.push("【连线】出线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '脚本执行', list: result});
  }
};

const validateReply = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
    if (!item.sourceParamId) {
      result.push(`【输入变量】第 ${index + 1} 行【变量来源】不能为空！`)
    }
  });
  if (!node.data.content) {
    result.push(`【回复内容】不能为空！`)
  }
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '直接回复', list: result});
  }
};

const validateEnd = (node) => {
  const result = [];
  (node.data.inputList || []).forEach((item, index) => {
    if (!item.name) {
      result.push(`【输入变量】第 ${index + 1} 行【变量名称】不能为空！`)
    }
    if (!item.sourceParamId) {
      result.push(`【输入变量】第 ${index + 1} 行【变量来源】不能为空！`)
    }
  });
  if (node.data.ifText === '1' && !node.data.content) {
    result.push(`【返回文本】不能为空！`)
  }
  if (!edges.value.find(edge => edge.target === node.id)) {
    result.push("【连线】入线未建立！");
  }
  if (result.length > 0) {
    errorList.value.push({name: node.label || '结束', list: result});
  }
};

watch(() => currNode.value?.data, () => {
  validateData();
}, {deep: true});

watch(() => edges.value, () => {
  validateData();
});

onMounted(async() => {
  initialized.value = true;
  await loadData();
  await nextTick(() => initialized.value = false);
  validateData();
});
</script>

<style>
.popover-box.el-popper {
  padding: 0;
}
.margin-top-50 {
  margin-top: 50px;
}
</style>

<style scoped lang="scss">
.editor-box {
  height: 100vh;
  display: flex;
  flex-direction: column;
  .header {
    height: 50px;
    line-height: 50px;
    padding: 0 20px;
    flex-shrink: 0;
    display: flex;
    border-bottom: 1px solid rgba(6, 6, 6, 0.06);;
    .header-name {
      flex-grow: 1;
      font-size: 16px;
      color: #333;
      font-weight: 700;
    }
    .header-button {
      justify-content: flex-end;
      .ac-icon {
        margin-right: 5px;
      }
    }
  }
  .vue-flow {
    flex-grow: 1;
    :deep(.vue-flow__edge-custom) {
      .ac-icon {
        display: none;
      }
      &:hover, &.selected {
        .ac-icon {
          display: block;
          background-color: #1890ff;
          padding: 1px;
          border-radius: 20px;
          path {
            stroke: white!important;
          }
        }
        path {
          stroke: #1890ff!important;
        }
      }
      &.success path {
        stroke: #67C23A!important;
      }
    }
  }
  .vue-flow__node {
    .vue-flow__handle {
      visibility: hidden;
      width: 0;
      height: 0;
      border: none;
      background: transparent;
    }
    .vue-flow__handle {
      .ac-icon {
        cursor: crosshair;
      }
    }
    .vue-flow__handle.left {
      .ac-icon {
        display: flex;
        margin-top: -6px;
        margin-right: 10px;
      }
    }
    .vue-flow__handle .right {
      position: relative;
      .ac-icon {
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        margin-top: -6px;
        margin-left: -12px;
      }
    }
  }
}
.popover-box-item {
  .popover-item {
    display: flex;
    cursor: pointer;
    align-items: center;
    margin: 3px 0;
    padding: 12px;
    min-height: 28px;
    .icon-box {
      padding: 6px;
      margin-right: 8px;
      border-radius: 5px;
    }
    .name-box {
      color: #333;
      font-size: 16px;
      font-weight: 600;
    }
  }
}
.popover-item:hover {
  background-color: #F0F0F0;
}
.custom-node {
  min-width: 200px;
  max-width: 200px;
  padding: 0 5px;
  .node-box {
    display: flex;
    flex-direction: column;
    position: relative;
    padding: 5px 12px;
    border-radius: 8px;
    background-color: #fff;
    border: 2px solid transparent;
    box-shadow: 0 2px 4px #d6d6d6;
    transition: box-shadow .15s;
    .node-header {
      display: flex;
      flex-direction: row;
    }
    .title {
      flex-grow: 1;
      display: flex;
      align-items: center;
      margin: 3px 0;
      .icon-box {
        padding: 6px;
        margin-right: 8px;
        border-radius: 5px;
      }
      .name-box {
        color: #333;
        font-size: 14px;
        font-weight: 600;
      }
    }
    .button {
      margin-top: -3px;
      margin-left: 20px;
      display: flex;
      cursor: pointer;
      align-items: center;
    }
    .classifier-item {
      display: flex;
      flex-direction: column;
      text-align: right;
      .classifier-name {
        height: 22px;
        line-height: 22px;
        font-weight: bolder;
        color: #333;
      }
      .classifier-content {
        height: 22px;
        line-height: 22px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        color: #999;
      }
    }
  }
}
.custom-node.active,
.custom-node:hover {
  .node-box {
    border-color: #1890ff;
  }
  .vue-flow__handle {
    visibility: visible;
    color: #1890ff;
  }
}
.custom-node:hover {
  .node-box {
    border-color: #1890ff;
    .button {
      .ac-icon {
        visibility: visible;
      }
    }
  }
}
.custom-node.success {
  .node-box {
    border-color: #67C23A;
  }
}
.custom-node.error {
  .node-box {
    border-color: #F56C6C;
  }
}
.drawer-body {
  padding: 15px 15px 30px 15px;
  height: calc(100vh - 100px);
  overflow: auto;
  .header {
    display: flex;
    margin-bottom: 10px;
    .title {
      flex-grow: 1;
      display: flex;
      align-items: center;
      margin: 3px 0;
      min-height: 32px;
      .icon-box {
        padding: 6px;
        margin-right: 8px;
        border-radius: 5px;
      }
      .name-box {
        flex-grow: 1;
        color: #333;
        font-size: 16px;
        font-weight: 600;
        display: flex;
        align-items: center;
        .ac-icon {
          cursor: pointer;
          margin-left: 8px;
        }
      }
    }
  }
}
.ellipsis {
  margin-top: 8px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.error-box {
  .title {
    font-weight: bolder;
    padding-bottom: 3px;
    border-bottom: 1px solid #d2d2d2;
  }
  .error-title {
    font-size: 16px;
    font-weight: bolder;
    margin-top: 15px;
    margin-bottom: 5px;
  }
  .error-info {
    padding-bottom: 3px;
  }
  .empty {
    margin-top: 15px;
  }
}
.debug-box {
  .success {
    color: #67C23A;
  }
  .failure {
    color: #F56C6C;
  }
  .line {
    margin-bottom: 8px;
  }
  .json-box {
    margin-top: 5px;
    white-space: pre;
    border-radius: 8px;
    background-color: #f5f5f5;
    padding: 12px;
    margin-bottom: 12px;
    pre {
      overflow: auto;
      white-space: pre-wrap;
      max-height: 300px;
    }
  }
}
.track-box {
  .title {
    display: flex;
    flex-direction: row;
    .node-name {
      flex-grow: 1;
      font-size: 15px;
      font-weight: bolder;
    }
    .error {
      color: #F56C6C;
    }
    .time {
      margin-right: 8px;
    }
  }
  .line {
    margin-bottom: 8px;
  }
  .json-box {
    white-space: pre;
    border-radius: 12px;
    background-color: #f5f5f5;
    padding: 12px;
    margin-bottom: 12px;
    pre {
      overflow: auto;
      white-space: pre-wrap;
      max-height: 300px;
    }
  }
}
</style>